#include "service.h"


// creates binary tree with information about files contented in archive

int buildArfileList(FILE *f, arfile **root)
{
    size_t offset = 0, left_to_read;
    int i, j;

    fseek(f, 0, SEEK_END);
    left_to_read = ftell(f) - 2; // -2 because of HZ and relative offset
    fseek(f,2,SEEK_SET);

    do
    {
	arfile *new_element = malloc(sizeof (arfile));
	new_element->name[0] = '\0';
	new_element->left = new_element->right = NULL;

	int symbols_read = 0;

	new_element->absolute_offset = offset;
	fread(&(new_element->offset), sizeof (new_element->offset), 1, f);
	offset += new_element->offset;

	for (j = 0, i=1; (char) i != '\0'; j++)
	{
	    // file is definately corrupted
	    if(j==MAX_FILENAME_LENGTH)
	    {
		new_element->name[MAX_FILENAME_LENGTH-1]='\0';
		break;
	    }
	    i = fgetc(f);
	    new_element->name[j] = (unsigned char) i;
	    symbols_read++;
	}

	fread(&(new_element->hash), sizeof (new_element->hash), 1, f);

	// going back
	fseek(f, -sizeof (new_element->hash) - symbols_read - sizeof (new_element->offset), SEEK_CUR);

	// next "header"
	fseek(f, new_element->offset, SEEK_CUR);

	// indicator for EOF
	left_to_read -= new_element->offset;

	if (-1 == addToArfileTree(root, new_element))
	    return -1;

    }
    while (left_to_read);

    return 0;
}

// checks if FILE is a valid HZ archive
// if file format becomes more complicated that part is bound to be extended

int testIfRightArchiveFormat(FILE *f, unsigned char name[])
{
    int buffer;

    rewind(f);
    // checking HZ header
    buffer = fgetc(f);
    if (buffer != 'H')
    {
	print_error(INVALID_ARC_OR_DAMAGED, name);
	return 1;
    }

    buffer = fgetc(f);
    if (buffer != 'Z')
    {
	print_error(INVALID_ARC_OR_DAMAGED, name);
	return 1;
    }

    rewind(f);

    return 0;
}

// open file in binary read mode printing error if needed

inline FILE *openFileRB(unsigned char* name)
{
    FILE *f;
    if (!(f = fopen((char*) name, "rb")))
    {
	print_error(OPENING_FILE, name);
	return NULL;
    }
    return f;
}

inline FILE *openFileWB(unsigned char *name)
{
    FILE *f;
    if (!(f = fopen((char*) name, "w+b")))
    {
	print_error(OPENING_FILE, name);
	return NULL;
    }
    return f;
}
// search by filename in binary search tree

int findFileInArchive(unsigned char* needle, arfile *root,
		      size_t *absOffset, size_t *relOffset)
{
    while (root)
    {

	if (!strcmp((char*) needle, (char*) root->name))
	{
	    if (absOffset != NULL)
		*absOffset = root->absolute_offset;
	    if (relOffset != NULL)
		*relOffset = root->offset;
	    return 0;
	}

	if (strcmp((char*) needle, (char*) root->name) > 0)
	{
	    root = root->right;
	    continue;

	}

	if (strcmp((char*) needle, (char*) root->name) < 0)
	{
	    root = root->left;
	    continue;
	}
    }
    return 1;
}

int addToArfileTree(arfile **root, arfile *in_node)
{
    if ((*root))
    {
	if (strcmp((char*) in_node->name, (char*) (*root)->name) > 0)
	{
	    if (-1 == addToArfileTree(&(*root)->right, in_node))
		return -1;
	}
	else
	{
	    if (strcmp((char*) in_node->name, (char*) (*root)->name) < 0)
	    {
		if (-1 == addToArfileTree(&(*root)->left, in_node))
		    return -1;
	    }
	    else
	    {
		free(in_node);
		return -1;
	    }
	}

    }
    else
    {
	*root = in_node;
	return 0;
    }
    return 0;
}


//instead of CRC. works faster, eats less

unsigned long adler32(FILE *f)
{
    unsigned long a = 1, b = 0;
    int var;
    rewind(f);
    do
    {
	var = fgetc(f);
	a = (a + (char) var) % MOD_ADLER;
	b = (b + a) % MOD_ADLER;
    }
    while (var != EOF);

    return (b << 16) | a;
}

void sel_sort(size_t *mainArray, size_t *subArray, int num)
{
    size_t temp_var1, temp_var2;
    int i, j, min;
    for (i = 0; i < num - 1; i++)
    {
	min = i;
	for (j = i + 1; j < num; j++)
	{
	    if (mainArray[j] < mainArray[min])
		min = j;
	}
	temp_var1 = mainArray[i];
	temp_var2 = subArray[i];

	mainArray[i] = mainArray[min];
	subArray[i] = subArray[min];

	mainArray[min] = temp_var1;
	subArray[min] = temp_var2;
    }
}


// list sorts itself during the procces of adding nodes

void add_to_list(list **head, node *data)
{
    list *t = (list*) malloc(sizeof (list));
    t->next = t->prev = NULL;
    t->tree = data;

    if (!(*head))
    {
	(*head) = t;
    }
    else
    {
	list *p;
	if (!(*head)->next)
	{
	    if ((*head)->tree->weight < t->tree->weight)
	    {
		(*head)->next = t;
		t->prev = (*head);
	    }
	    else
	    {
		t->tree = (*head)->tree;
		(*head)->tree = data;
		t->prev = (*head);
		(*head)-> next = t;
	    }
	    return;
	}

	for (p = (*head); p; p = p->next)
	{
	    if (p->tree->weight > t->tree->weight)
	    {
		if (!p->prev)
		{
		    t->next = (*head)->next;
		    t->prev = (*head);
		    (*head)->next->prev = t;
		    (*head)->next = t;
		    t->tree = (*head)->tree;
		    (*head)->tree = data;
		}
		else
		{
		    p->prev->next = t;
		    t->prev = p->prev;
		    t->next = p;
		    p->prev = t;
		}
		return;
	    }
	}

	for (p = (*head); p->next; p = p->next);
	t->prev = p;
	p->next = t;
    }
}

node *get_from_list(list **head)
{
    node *var;
    if (!(*head))
    {
	var = (node*) malloc(sizeof (node));
	var->weight = 0; //mark that the node (not leaf) is a root of HT
	return var;
    }
    else
    {
	var = (*head)->tree;
	if ((*head)->next)
	{
	    list *list_var = (*head);
	    (*head) = (*head)->next;
	    (*head)->prev = NULL;
	    free(list_var);
	}
	else
	{
	    (*head) = NULL;
	}
    }
    return var;
}

// postfix traversal with the cruel murder of all the poor leafs ha-ha-ha

void annihilate_tree(node **root)
{
    if (!(*root))
	return;

    if ((*root)->right)
    {
	if ((!((*root)->right->left)) && (!((*root)->right->right)))
	{
	    /*
	    #ifdef DEBUG
				    printf("[%u] ", (*root)->right->data);
	    #endif
	     */
	    free((*root)->right);
	    (*root)->right = NULL;
	}
    }
    if ((*root)->left)
    {
	if ((!((*root)->left->left)) && (!((*root)->left->right)))
	{
	    /*
	    #ifdef DEBUG
				    printf("[%u] ", (*root)->left->data);
	    #endif
	     */
	    free((*root)->left);
	    (*root)->left = NULL;
	}
    }

    annihilate_tree(&(*root)->left);
    annihilate_tree(&(*root)->right);
}

void annihilate_arfiletree(arfile *root)
{
    if (root == NULL)
	return;

    if (root->left)
	annihilate_arfiletree(root->left);
    if (root->right)
	annihilate_arfiletree(root->right);

    free(root);
}


// function was written just for fun and is not really needed for *nix
// it checks whether the string is a valid filename for win platform

int validate_filename(unsigned char input[])
{
    int i;
    char pattern_chars[] = "<>:\"/\\|?*|";
    char pattern_names[PROHIBITED_NAMES_QUANTITY][5] = {"CON", "PRN", "AUX", "NUL",
	"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1",
	"LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
    if (!input)
	return 1;

    for (i = 0; pattern_chars[i]; i++)
	if (strchr((char*) input, (int) pattern_chars[i]))
	    return 1;

    for (i = 0; i < PROHIBITED_NAMES_QUANTITY; i++)
	if (!strcmp((char*) input, pattern_names[i]))
	    return 1;
    return 0;
}

// counts how many not-null cells are present in the array

inline unsigned char count_symbols(unsigned long *array)
{
    int i;
    unsigned char sum = 0;
    for (i = 0; i < 256; i++)
	if (array[i])
	    sum++;
    return sum;
}

